개요
Spring Boot Project에 actuator를 적용한 뒤, prometheus micrometer를 적용했을 때 WebMvcConfigurationSupport
를 customizing 하게 되면 발생하는 문제에 대한 해결책이다.
해당 문제를 보려면 일단 Spring Boot Project를 생성하자. (Spring boot Initializer를 사용하면 편리하다)
그 뒤, 아래의 dependency를 추가한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer Prometheus registry -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
application.properties에 아래 내용을 추가한다(추가하지 않으면 /actuator/prometheus가 exposure되지 않아서 접근할 수 없다)
management.endpoints.web.exposure.include=*
그렇게 되면 아래의 path로 prometheus exporter 를 접근할 수 있게 된다(기본 8080 포트 사용 시)
localhost:8080/actuator/prometheus
문제
자, 이 상태로 무언가 WebMvcConfigurationSupport
의 ObjectMapper를 수정하고 싶을 때(default serialize, deserialize 등의 행동을 바꾸고자 할 때 간혹 사용된다) 아래와 같은 method를 override를 하게 된다.
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
public void configureMessageConverters(List converters) {
final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
// ObjectMapper Settings(JODA)
objectMapper.registerModule(new JodaModule());
objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.
WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
converter.setObjectMapper(objectMapper);
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
// Add settings to converter, builder
converters.add(converter);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
위와 같은 override를 해서 Spring 내부의 MessageConverter 를 수정했을 경우 API 등은 잘 동작할 수 있다.
하지만, prometheus exporter에서 사용하는 Content Type은 text/plain
을 사용하게 된다.
백문이 불여일견, 위 Config class와 함께 prometheus exporter 페이지를 접근해보자(localhost:8080/actuator/prometheus)
위와 같이 Http 406 Not Acceptable
과 함께 나오지 않는다.
해결방법
MappingJackson2HttpMessageConverter
에 Content Type을 지정한다
JSON은 정상적으로 나오니, Content Type에 TEXT/PLIAN
을 추가하면 되지 않을까?
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
엇 무언가 나온다. 근데 포맷팅이 이상하다
TEXT/HTML의 내용을 JSON형식으로 마셜링을 해버린것이다..;
이렇게 된 경우, prometheus server에서 이를 수집할 수가 없다.(formatting이 맞지 않기 때문)
사용하지 않도록 하자 ..
StringHttpMessageConverter
를 converter에 추가한다
converters.add(new StringHttpMessageConverter());
를 아까 생성한 Config 클래스에 추가를 해주자.
추가가 잘 되면 드디어! 정상적인 format으로 나오게 된다.
그럼 StringHttpMessageConverter
는 뭘하는놈일까
내부를 열어보면
public StringHttpMessageConverter(Charset defaultCharset) {
super(defaultCharset, new MediaType[]{MediaType.TEXT_PLAIN, MediaType.ALL});
this.writeAcceptCharset = true;
}
기본적으로 TEXT_PLAIN 과 모든 MediaType을 가지고 생성이 된다.
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
if (this.writeAcceptCharset) {
outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets());
}
Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}
그리고 해당 함수에서 Response로 값을 write 하는 부분을 찾아보게 되면 위와 같이 String으로 convert를 해서 내보내는 것을 알 수 있다.
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = this.getContentTypeCharset(inputMessage.getHeaders().getContentType());
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
읽을땐 역시 반대로 String으로 convert를 한 뒤 String으로 전달을 해준다.
'Development > Spring' 카테고리의 다른 글
Spring Redis Template Transaction (2) | 2021.09.09 |
---|---|
Spring RequestContextHolder (9) | 2020.07.05 |
Spring Password Encoder (4) | 2019.04.06 |
Dispatcher Servlet (0) | 2019.04.06 |
[JAVA/Spring] BeanUtils 관련 (2) | 2018.07.20 |