Spring Boot의 기본 로그 객체는 Logback으로 SLF4j의 구현체이다. Spring Boot 프로젝트를 실행하면 아래와 같이 LogBack의 기본 로그 포맷이 콘솔에 찍히게 된다.
업무에 사용되는 모니터링 시스템이나, 로깅 시스템에 따라 로그 포맷을 바꿔야 하는 경우가 있는데 이러한 경우 다음과 같은 방법으로 로그 포맷을 변경할 수 있다.
1. application파일에서 로깅 포맷을 변경 (저는 yml 형태로 application 파일을 설정합니다.)
[ resources/application.yml ]
logging:
pattern:
console: "%cyan([GatewayServer]) - %magenta([%d{yyyy:mm:dd HH:MM:ss}]) - %highlight([%-5level]) - %green([%logger{35}]) - %msg%n"
현재 사용중인 로그 포맷으로 각 항목별로 한눈에 들어오도록 색상 옵션을 추가하여 로그가 찍히도록 하였다. 개인적으로는 로그 앞에 현재 애플리케이션 이름을 붙여 로그 시스템에서 해당 로그가 발생된 서버를 구분하고 있다.
2. logback.xml 파일을 생성, 해당 파일에서 기본 로그 포맷 변경
[ resources/logback.xml ]
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[logback.xml] %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
컬러 옵션을 주지 않아 테마의 기본 컬러로 콘솔에 로그가 찍히게 된다. (Material Theme 사용중입니다.)
3. Log 형식을 JSON 형태로 바꾸기
다른 third party 라이브러리를 활용하여 로그를 처리할 때, 로그 포맷이 JSON 형태인 경우 전처리 또는 파싱 과정이 좀 더 단순해진다. JSON 형태로 로그 포맷을 변경하기 위해서는 다음과 같이 logback 라이브러리를 추가한다.
[ build.gradle ]
// log
compile group: 'ch.qos.logback.contrib', name: 'logback-json-classic', version: '0.1.5'
compile group: 'ch.qos.logback.contrib', name: 'logback-jackson', version: '0.1.5'
그다음 logback.xml 파일에서 위에서 추가된 라이브러리를 통해 다음과 같이 JsonLayout을 설정해준다.
[ logback.xml ]
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSX</timestampFormat>
<timestampFormatTimezoneId>Etc/UTC</timestampFormatTimezoneId>
<appendLineSeparator>true</appendLineSeparator>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
이후 애플리케이션을 구동하면 다음과 같이 json 포맷으로 로그가 찍히는 것을 볼 수 있다.
로그 메시지를 json 형태에 맞추어 예쁘게 출력하기 위해서는 다음 구문을 logback.xml에 추가하면 된다.
[ logback.xml ]
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSX</timestampFormat>
<timestampFormatTimezoneId>Etc/UTC</timestampFormatTimezoneId>
<appendLineSeparator>true</appendLineSeparator>
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
<prettyPrint>true</prettyPrint>
</jsonFormatter>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
위와 같이 설정한 뒤에 어플리케이션을 다시 구동하면, 메시지 별로 줄 바꿈이 되며, JSON 포맷에 맞추어 예쁘게 출력된다.
3. MDC(Mapped Diagnostic Context)를 활용하여 API 데이터도 같이 JSON 형태로 로깅하기
API를 요청할 때마다 응답 값을 JSON 형태로 로깅하고 싶다면 다음과 같이 설정하면 된다.
[ UserController ]
package com.virnectremote.gateway.controller;
import com.virnectremote.gateway.common.ResponseMessage;
import com.virnectremote.gateway.dto.UserDto;
import com.virnectremote.gateway.enums.ErrorMessage;
import com.virnectremote.gateway.exception.RequestParameterException;
import com.virnectremote.gateway.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* Project: gateway
* DATE: 2019-04-19
* AUTHOR: JohnMark (Chang Jeong Hyeon)
* EMAIL: practice1356@gmail.com
* DESCRIPTION: User API Request Controller
*/
@Slf4j(topic = "UserController")
@RestController
@RequestMapping("/user")
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public String home() {
return "Please Contact Me JohnMark <practice1356@gmail.com>";
}
@PostMapping(value = "/register", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity register(@ModelAttribute @Valid UserDto.Register register, BindingResult result) {
if (result.hasErrors()) {
log.error(result.getFieldError().getObjectName() + "." + result.getFieldError().getField() + " : " + result.getFieldError().getDefaultMessage());
throw new RequestParameterException(ErrorMessage.REQUEST_PARAM_ERROR);
}
ResponseMessage registedUserInfo = this.userService.register(register);
return ResponseEntity.ok(registedUserInfo);
}
@PostMapping("/login")
public ResponseEntity login(@RequestBody @Valid UserDto.Login userLogin, BindingResult result) {
if (result.hasErrors()) {
log.error(result.getFieldError().getObjectName() + "." + result.getFieldError().getField() + " : " + result.getFieldError().getDefaultMessage());
throw new RequestParameterException(ErrorMessage.REQUEST_PARAM_ERROR);
}
ResponseMessage response = this.userService.login(userLogin);
// MDC put user info
MDC.put("email",userLogin.getEmail());
MDC.put("name",((UserDto.UserInfo) response.getData().get("user")).getName());
log.info("login success");
return ResponseEntity.ok(response);
}
}
Controller 단에서 MDC Context에 저장한 사용자 식별 정보는 다음과 같이 mdc 오브젝트 엘리먼트에 값으로 출력되며
log.info("login success") 구문으로 출력한 로그는 message 엘리먼트에 출력이 된다.
{
"timestamp" : "2019-05-28T00:59:48.436Z",
"level" : "INFO",
"thread" : "http-nio-8080-exec-1",
"mdc" : {
"name" : "장정현",
"email" : "test@test.com"
},
"logger" : "UserController",
"message" : "login success",
"context" : "default"
}
이와 같은 방법을 쓰면 Controller에서 각 API 요청 별로 ID를 부여하고 MDC를 통해 API 데이터를 저장하면 API별 응답 값을 구분하여 로그로 출력할 수 있다
[ 참고 ]
조대협 Spring Boot에서 JSON 포맷 로깅과 MDC 사용하기
'Programming > Spring' 카테고리의 다른 글
[SpringBoot] Redis Channel Subscribe with MessagePack (0) | 2019.09.18 |
---|---|
[SpringBoot] Redis Publish Channel Subscribe (0) | 2019.09.18 |
[Spring Boot] @Schedule로 스케줄 프로그래밍 하기 (0) | 2019.08.22 |
[SpringBoot] 에러 로그 모니터링 with Sentry (0) | 2019.08.04 |
[SpringBoot] LocalDateTime Json Serialized (2) | 2019.06.19 |